// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "coin2/base/string_util.h"
#include "coin2/exchange/base/symbology/spot_product.h"
#include "coin2/exchange/base/symbology/util.h"
#include "coin2/exchange/mexc/symbology/currency.h"

namespace coin2::exchange::mexc::symbology {

class MexcProduct : public base::symbology::SpotProduct<MexcCurrency, MexcProduct>,
                    public base::symbology::ProductFromStrMixin<MexcProduct> {
 public:
  typedef SpotProduct<MexcCurrency, MexcProduct> BaseProduct;

  MexcProduct(const MexcCurrency& base, const MexcCurrency& quote)
      : SpotProduct(
            base,
            quote,
            ToMexcV2NativeProduct(base, quote),
            ToMexcNativeProduct(base, quote),
            base::market::ExchangeType::Mexc) {}

 public:
  static std::unique_ptr<MexcProduct> FromNativeStrImpl(
      const std::string& native_product,
      int64_t resolve_timestamp) {
    std::unique_ptr<MexcCurrency> base_ptr;
    std::unique_ptr<MexcCurrency> quote_ptr;
    bool success = false;
    if (native_product.find("_") == std::string::npos) {
      success = ParseMexcNativeProduct(native_product, resolve_timestamp, &base_ptr, &quote_ptr);
    } else {
      success = ParseMexcV2NativeProduct(native_product, resolve_timestamp, &base_ptr, &quote_ptr);
    }
    if (success) {
      return std::make_unique<MexcProduct>(*base_ptr, *quote_ptr);
    } else {
      return {};
    }
  }

  static std::string ToMexcV2NativeProduct(
      const base::symbology::ICurrency& base,
      const base::symbology::ICurrency& quote) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << base.native_currency(MarketType::Spot) << "_" << quote.native_currency(MarketType::Spot);
    return ss.str();
  }

 private:
  static bool ParseMexcNativeProduct(
      const std::string& product_str,
      int64_t resolve_timestamp,
      std::unique_ptr<MexcCurrency>* base_ptr,
      std::unique_ptr<MexcCurrency>* quote_ptr) {
    using base::symbology::HasValue;
    static const std::string quotes[] = {
        "USDT",
        "USDC",
        "ETH",
        "BTC",
        "BUSD",
        "TRX",
        "ADA",
        "EOS",
        "TRYB",
        "USDP",
        "DOGE",
    };

    const std::string& product_str_upper = StringToUpperCopy(product_str);

    for (const std::string& quote_cand : quotes) {
      if (StringEndsWith(product_str_upper, quote_cand)) {
        size_t base_len = product_str_upper.size() - quote_cand.size();
        std::string base = product_str_upper.substr(0, base_len);
        *base_ptr = MexcCurrency::FromNativeStrNoThrow(base, resolve_timestamp);
        *quote_ptr = MexcCurrency::FromNativeStrNoThrow(quote_cand, resolve_timestamp);
        return true;
      }
    }
    return false;
  }

  static bool ParseMexcV2NativeProduct(
      const std::string& product_str,
      int64_t resolve_timestamp,
      std::unique_ptr<MexcCurrency>* base_ptr,
      std::unique_ptr<MexcCurrency>* quote_ptr) {
    std::vector<std::string> splitted;
    splitted = Split(product_str, "_");
    if (splitted.size() != 2) {
      return false;
    }
    *base_ptr = MexcCurrency::FromNativeStrNoThrow(splitted[0], resolve_timestamp);
    *quote_ptr = MexcCurrency::FromNativeStrNoThrow(splitted[1], resolve_timestamp);
    return true;
  }

  static std::string ToMexcNativeProduct(const MexcCurrency& base, const MexcCurrency& quote) {
    using base::market::MarketType;
    std::stringstream ss;
    ss << base.native_currency(MarketType::Spot) << quote.native_currency(MarketType::Spot);
    return ss.str();
  }
};

}  // namespace coin2::exchange::mexc::symbology
